#include "lexer.h"

void Token::Dump()
{
    llvm::StringRef text(ptr, length);
    llvm::outs() << "{" << text << ", row = " << row << ", col = " << col << "}\n";
}

// number,     // [0-9]+
// indentifier,// 变量
// kw_int,     // int
// plus,       // +
// minus,      // -
// star,       // *
// slash,      // /
// equal,      // =
// l_parent,   // (
// r_parent,   // )
// semi,       // ;
// comma,      // ,
llvm::StringRef Token::GetSpellingText(TokenType tokenType)
{
    switch (tokenType)
    {
    case TokenType::kw_int:
        return "int";
    case TokenType::plus:
        return "+";
    case TokenType::minus:
        return "-";
    case TokenType::star:
        return "*";
    case TokenType::slash:
        return "/";
    case TokenType::equal:
        return "=";
    case TokenType::l_parent:
        return "(";
    case TokenType::r_parent:
        return ")";
    case TokenType::semi:
        return ";";
    case TokenType::comma:
        return ",";
    case TokenType::number:
        return "number";
    case TokenType::indentifier:
        return "indentifier";
    default:
        llvm::llvm_unreachable_internal();
    }
}

bool IsWhiteSpace(char ch)
{
    return ch == ' ' || ch == '\r' || ch == '\n';
}

bool IsDigit(char ch)
{
    return ch >= '0' && ch <= '9';
}

bool IsLetter(char ch)
{
    // a-z, A-Z, _
    return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch == '_');
}

void Lexer::NextToken(Token &token)
{
    // 过滤空格
    while (IsWhiteSpace(*BufPtr))
    {
        if (*BufPtr == '\n')
        {
            row += 1;
            LineHeadPtr = BufPtr + 1;
        }
        BufPtr++;
    }

    token.row = row;
    token.col = BufPtr - LineHeadPtr + 1;

    // 判断是否到结尾了
    if (BufPtr >= BufEnd)
    {
        token.tokenType = TokenType::eof;
        return;
    }

    token.ptr = BufPtr;
    token.length = 0;
    // 判断是否为数字
    if (IsDigit(*BufPtr))
    {
        int len = 0;
        int val = 0;
        while (IsDigit(*BufPtr))
        {
            val = val * 10 + *BufPtr++ - '0';
            token.length++;
        }
        token.value = val;
        token.tokenType = TokenType::number;
        token.type = CType::getIntTy();
    }
    else if (IsLetter(*BufPtr)) // 为变量
    {
        while (IsLetter(*BufPtr) || IsDigit(*BufPtr))
        {
            BufPtr++;
        }
        token.tokenType = TokenType::indentifier;
        token.length = BufPtr - token.ptr;
        llvm::StringRef text(token.ptr, BufPtr - token.ptr);
        if (text == "int")
        {
            token.tokenType = TokenType::kw_int;
        }
    }
    else // 为特殊字符
    {
        switch (*BufPtr)
        {
        case '+':
        {
            token.tokenType = TokenType::plus;
            token.length = 1;
            BufPtr++;
            break;
        }
        case '-':
        {
            token.tokenType = TokenType::minus;
            token.length = 1;
            BufPtr++;
            break;
        }
        case '*':
        {
            token.tokenType = TokenType::star;
            token.length = 1;
            BufPtr++;
            break;
        }
        case '/':
        {
            token.tokenType = TokenType::slash;
            token.length = 1;
            BufPtr++;
            break;
        }
        case '=':
        {
            token.tokenType = TokenType::equal;
            token.length = 1;
            BufPtr++;
            break;
        }
        case '(':
        {
            token.tokenType = TokenType::l_parent;
            token.length = 1;
            BufPtr++;
            break;
        }
        case ')':
        {
            token.tokenType = TokenType::r_parent;
            token.length = 1;
            BufPtr++;
            break;
        }
        case ';':
        {
            token.tokenType = TokenType::semi;
            token.length = 1;
            BufPtr++;
            break;
        }
        case ',':
        {
            token.tokenType = TokenType::comma;
            token.length = 1;
            BufPtr++;
            break;
        }
        default:
        {
            diagEngine.Report(llvm::SMLoc::getFromPointer(BufPtr), diag::err_unknown_char, *BufPtr);
            BufPtr++;
            break;
        }
        }
    }
}

void Lexer::SaveState()
{
    state.LineHeadPtr = LineHeadPtr;
    state.BufPtr = BufPtr;
    state.BufEnd = BufEnd;
    state.row = row;
}
void Lexer::RestoreState()
{
    LineHeadPtr = state.LineHeadPtr;
    BufPtr = state.BufPtr;
    BufEnd = state.BufEnd;
    row = state.row;
}